#ifndef	__UNISTRING_H__
#define	__UNISTRING_H__

namespace BFX
{

//////////////////////////////////////////////////////////////////////////////
// The internal shared string data.
typedef const UCHAR*	UCSTR;

//////////////////////////////////////////////////////////////////////////////

class BFX_API UniString
{
	friend class REFUniStringBuilder;
public:
	// Creates a new empty string.
	UniString();
	// Creates a new string with the characters copied in from utf-16 characters in a subarray,
	// or entire zero-end string data if given length is -1.
	UniString(UCSTR pszStr, int nLength = -1);
	// Creates a new string by decoding the bytes data using specified charset.
	UniString(const BYTE* pBytes, int nLength, PCSTR pszEnc);
	// Creates a new string by decoding the sequence of bytes using specified charset.
	UniString(const SequenceT<BYTE> bytes, PCSTR pszEnc);
	// Creates a new string by other instance.
	UniString(const UniString& str);
	virtual ~UniString();

	// Returns the length of this string.
	int GetLength() const;
	bool IsEmpty() const;
	void SetEmpty();

	// Returns the char value at the specified index.
	UCHAR GetAt(int iIndex) const;
	// Encodes this string into a sequence of bytes using specified charset.
	SequenceT<BYTE> GetBytes(PCSTR pszEnc) const;
	// Returns a sequence of char that copied from string.
	SequenceT<UCHAR> GetChars() const;
	UCSTR GetData() const;

	bool Equals(const UniString& str) const;
	int CompareTo(const UniString& str, bool bIgnoreCase = false) const;

	bool StartsWith(const UniString& strPrefix) const;
	bool EndsWith(const UniString& strSuffix) const;

	// Returns the index of the first occurance of value in the current instance.
	int IndexOf(UCHAR ch) const;
	int IndexOf(const UniString& str) const;
	// Returns the index of the last occurance of value in the current instance.
	int LastIndexOf(UCHAR ch) const;
	int LastIndexOf(const UniString& str) const;

	// Removes a string of characters from both starts and ends of this string.
	UniString Trim() const;
	// Converts string in lower case.
	UniString ToLowercase() const;
	// Converts string in upper case.
	UniString ToUppercase() const;

	// Returns a substring of this string.
	UniString Substring(int iOffset) const;
	UniString Substring(int iOffset, int nLength) const;

	UniString Replace(UCHAR chTarget, UCHAR chReplacement) const;
	UniString Replace(const UniString& strTarget, const UniString& strReplacement) const;

	void Append(const UniString& strSrc);
	void Append(UCHAR chSrc);
	void Append(UCSTR pszSrc, int nLength);

	operator UCSTR() const throw()
	{
		return GetData();
	}
	UniString& operator=(const UniString& strSrc);
	UniString& operator+=(const UniString& strSrc);
	UniString& operator+=(UCSTR pszSrc);
	UniString& operator+=(UCHAR chSrc);

 	friend BFX_API bool operator==(const UniString& str1, const UniString& str2) throw();
	friend BFX_API bool operator!=(const UniString& str1, const UniString& str2) throw();

	friend BFX_API UniString operator+(const UniString& str1, const UniString& str2) throw();
	friend BFX_API UniString operator+(const UniString& str1, UCSTR psz2) throw();
	friend BFX_API UniString operator+(UCSTR psz1, const UniString& str2) throw();
	friend BFX_API UniString operator+(const UniString& str1, UCHAR ch2) throw();
	friend BFX_API UniString operator+(UCHAR ch1, const UniString& str2) throw();

protected:
	// This class represents a shared string data.
	class StringData : public REFObject
	{
	public:
		StringData(int nCapacity = 0);
		virtual ~StringData();
		void Capacity(int nCapacity);

		UCHAR*	m_pBuffer;
		int	m_nLength;
		int	m_nAllocLength;
	};

protected:
	UCHAR* GetBuffer(int nLength);
	void SetLength(int nLength);

private:
#ifdef	_MSC_VER
#pragma warning(push)
#pragma warning(disable:4251)
#endif	//	_MSC_VER
	REF<StringData>	m_pStringData;
#ifdef	_MSC_VER
#pragma warning(pop)
#endif	//	_MSC_VER
};

//////////////////////////////////////////////////////////////////////////////
// String conversions.
// NOTE: the bytes of UNICODE character is 2 or 4, depends on target system.

// Returns a UTF-16 string with specified ANSI string.
BFX_API UniString StringMakeU(const char* pszAnsi);
// Returns a copy of specified UTF-16 string.
BFX_API UniString StringMakeU(const UniString& strUtf16);
// Returns a UTF-16 string with specified UNICODE string.
BFX_API UniString StringMakeU(const wchar_t* pszUnicode);

// Returns a copy of specified ANSI string.
BFX_API String StringMakeA(const String& strAnsi);
// Returns a ANSI string with specified UTF-16 string.
BFX_API String StringMakeA(UCSTR pszUtf16);
// Returns a ANSI string with specified UNICODE string.
BFX_API String StringMakeA(const wchar_t* pszUnicode);

// Returns a UNICODE string with specified ANSI string.
BFX_API StringW StringMakeW(const char* pszAnsi);
// Returns a UNICODE string with specified UNICODE string.
BFX_API StringW StringMakeW(UCSTR pszUtf16);
// Returns a copy of specified UNICODE string.
BFX_API StringW StringMakeW(const StringW& strUnicode);

#define	USTR(str)	StringMakeU(str)
#define	CSTR(str)	StringMakeA(str)
#define	WSTR(str)	StringMakeW(str)

//////////////////////////////////////////////////////////////////////////////
// special versions of HashTraits for utf-16 string.

template<>
class HashTraits<UniString>
{
public:
	static int __cdecl GetHashCode(const UniString& key) throw()
	{
		int nHashCode = key.GetLength();
		// use key length to eliminate the range check
		for (int i = 0; i < key.GetLength(); i++)
			nHashCode += (nHashCode << 7 ) ^ key[i];
		// mix it a bit more
		nHashCode -= nHashCode >> 17;
		nHashCode -= nHashCode >> 11;
		nHashCode -= nHashCode >> 5;

		return nHashCode;
	}
	static bool __cdecl Equals(const UniString& key1, const UniString& key2) throw()
	{
		return key1 == key2;
	}
};

}	//	namespace BFX


#endif	//	__UNISTRING_H__
